/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package graph;
import structures.Texts;
import java.util.*;
import java.io.RandomAccessFile;
/**
 *
 * @author Gonzalo
 */
public class Graph {
    private long speed=0;
    private long speed_intervalo=0;
    private long speed_intervalo_k=0;

    private Texts texts;
    private RandomAccessFile file_nodes;
    private HashMap<String,Edges> hash_edges;
    private HashMap<String,Attributes> hash_attr;

    private String label_actual="";
    private String label_attr_actual="";
    private Edges arcos=null;
    private Edges arcos_=null;
    private Attributes atributos=null;
    private long num_nodes=0;

    public Graph(RandomAccessFile _nodes,HashMap<String,Edges> _hash_edges,HashMap<String,Attributes> _hash_attr,Texts _texts){
        texts=_texts;
        file_nodes=_nodes;
        num_nodes=countNodes();
        hash_edges=_hash_edges;
        hash_attr=_hash_attr;
    }
    public long newNodes(long n){
        num_nodes=num_nodes+n;
        try{
            file_nodes.seek(0);
            file_nodes.writeLong(num_nodes);
        }catch(Exception e){}
        return num_nodes;
    }
    public long newNode(){
        return newNodes(1);
    }
    public long countNodes(){
        if(num_nodes>0)
            return num_nodes;
        try{
            file_nodes.seek(0);
            num_nodes=file_nodes.readLong();
            return num_nodes;
        }catch(Exception e){
            return 0;
        }
    }
    
    public boolean addEdge(String a,long desde,long hasta){
        if(!setLabel(a) || desde>num_nodes || hasta>num_nodes || desde<1 || hasta<1) return false;
        arcos.put(desde, hasta);
        arcos_.put(hasta, desde);
        return true;
    }

    public boolean setAttribute(long nodo,String attr,String v){
        if(!setLabelAttr(attr) || nodo>num_nodes || nodo<1) return false;
        if(atributos.isTypeString())
            atributos.set(nodo,texts.get(v));
        else
            atributos.set(nodo,Long.parseLong(v));
        return true;
    }
    public boolean setAttribute(long nodo,String attr,long v){
        if(!setLabelAttr(attr) || nodo>num_nodes || nodo<1) return false;
        atributos.set(nodo,v);
        return true;
    }
    public Nodes getNodosWhere(String attr,String op,String o){
        //debug("getNodosWhere "+attr);
        Nodes out=new Nodes();
        if(!setLabelAttr(attr)) return out;
        if(atributos.isTypeString())
            atributos.searchAttr(texts.get(o));
        else
            atributos.searchAttr(Long.parseLong(o));
        for(atributos.initValue();atributos.notNullValue();atributos.nextValue()){
            out.add(atributos.getValue());
        }
        return out;
    }
    public long findNodoWhere(String attr,String o){
        //debug("getNodosWhere "+attr);
        if(!setLabelAttr(attr)) return 0;
        if(atributos.isTypeString())
            atributos.searchAttr(texts.get(o));
        else
            atributos.searchAttr(Long.parseLong(o));
        atributos.initValue();
        return atributos.getValue();
    }
    public long findNodoWhere(String attr,long o){
        //debug("getNodosWhere "+attr);
        if(!setLabelAttr(attr)) return 0;
        atributos.searchAttr(o);
        atributos.initValue();
        return atributos.getValue();
    }
    public long getAttribute(long nodo,String attr){
        if(!setLabelAttr(attr) || nodo>num_nodes || nodo<1) return 0;
        return atributos.getAttr(nodo);
    }
    public String getAttribute(long nodo,String attr,boolean type){
        if(!setLabelAttr(attr) || nodo>num_nodes || nodo<1) return "";
        return texts.get(atributos.getAttr(nodo));
    }
    public Nodes getTailsWithHeadsIn(String label){//getTails
        Nodes out=new Nodes();
        if(!setLabel(label)) return out;
        for(arcos.initKey();arcos.notNullKey();arcos.nextKey()){
            arcos.initValue();
            if(arcos.notNullValue()){
                out.add(arcos.getKey());
            }
        }
        return out;
    }
    public Nodes getHeadsWithTailsIn(String label){//getHeads
        Nodes out=new Nodes();
        if(!setLabel(label)) return out;
        for(arcos_.initKey();arcos_.notNullKey();arcos_.nextKey()){
            arcos_.initValue();
            if(arcos_.notNullValue()){
                out.add(arcos_.getKey());
            }
        }
        return out;
    }
    public Nodes getTailsWithHeadsIn(String label,Nodes N){//Discrimina Umbral
        //debug("getTailsWithHeadsIn "+label+" "+N.count());
        if(!setLabel(label)) return new Nodes();
        if(N.isUniverse())
            return getTailsWithHeadsIn(label);
        else if(umbral(N.count(),label)){
            //debug("getTailWithHeadInGrande "+label+" "+N.count());
            return getTailWithHeadInGrande(arcos,N);
        }else{
            //debug("getHeadWithTailInChico "+inverso(label)+" "+N.count());
            return getHeadWithTailInChico(arcos_,N);
        }
    }
    public Nodes getHeadsWithTailsIn(String label,Nodes N){//Discrimina Umbral
        if(!setLabel(label)) return new Nodes();
        if(N.isUniverse())
            return getHeadsWithTailsIn(label);
        else if(umbral(N.count(),label)){
            //debug("getTailWithHeadInGrande "+inverso(label)+" "+N.count());
            return getTailWithHeadInGrande(arcos_,N);
        }else{
            //debug("getHeadWithTailInChico "+label+" "+N.count());
            return getHeadWithTailInChico(arcos,N);
        }
    }

    public Nodes getTailWithHeadInGrande(Edges edges,Nodes N){
        Nodes out = new Nodes();
        for(edges.initKey();edges.notNullKey();edges.nextKey()){//arcos.gdb
            for(edges.initValue();edges.notNullValue();edges.nextValue()){
                if(N.contains(edges.getValue())){
                   out.add(edges.getKey());
                   break;
                }
            }
        }
        for(edges.initLazy();edges.notNullLazy();edges.nextLazy()){ //arcos.gdb.aux
            for(edges.initLazyValue();edges.notNullValue();edges.nextLazyValue()){
                if(N.contains(edges.getValue())){
                   out.add(edges.getKey());
                   break;
                }
            }
        }
        return out;
    }
    public Nodes getHeadWithTailInChico(Edges edges,Nodes N){
        Nodes out = new Nodes();
        for(N.initSorter();N.notNull();N.next()){
            edges.searchKey(N.getActual());
            for(edges.initValue();edges.notNullValue();edges.nextValue()){//arcos_.gdb
                out.add(edges.getValue());
            }
        }
        for(edges.initLazy();edges.notNullLazy();edges.nextLazy()){ //arcos_.gdb.aux
            for(edges.initLazyValue();edges.notNullValue();edges.nextLazyValue()){
                out.add(edges.getValue());
            }
        }

        return out;
    }

    public boolean setLabel(String label){
        if(!hash_edges.containsKey(label)) return false;
        if(label_actual.compareTo(label)==0) return true;
        arcos=hash_edges.get(label);
        arcos_=hash_edges.get(inverso(label));
        label_actual=label;
        return true;
    }
    public boolean setLabelAttr(String label){
        if(!hash_attr.containsKey(label)) return false;
        if(label_attr_actual.compareTo(label)==0) return true;
        atributos=hash_attr.get(label);
        label_attr_actual=label;
        return true;
    }
    public String inverso(String label){return label+"_";}
    public boolean umbral(long num,String a){return num>100000;}

    public void flush(){
        Iterator<Edges> it=hash_edges.values().iterator();
        while(it.hasNext())
            it.next().flush();
        Iterator<Attributes> ita=hash_attr.values().iterator();
        while(ita.hasNext())
            ita.next().flush();
        //texts.flush();
    }
    public String printNode(long nodo){
        Iterator<Map.Entry<String,Attributes>> it=hash_attr.entrySet().iterator();
        Map.Entry<String,Attributes> attr;
        String out="node("+nodo+")[ ";
        String v;
        long n;
        while(it.hasNext()){
            attr=it.next();
            if(attr.getValue().isTypeString())
                v=texts.get(attr.getValue().getAttr(nodo));
            else{
                n=attr.getValue().getAttr(nodo);
                if(n==0)
                    v="";
                else
                    v=""+n;
            }
            if(v.length()>0)
                out+=attr.getKey()+"="+v+" ";
        }
        out+="]";
        return out;
    }

    public void debug(String s){System.out.println(s);}
    public void debug(int i){System.out.println(""+i);}
    public void debug(long i){System.out.println(""+i);}
    public void debug(Exception ex){System.out.println(ex.toString());}
    public void speed(String s){if(speed==0) debug("Iniciando Speed"); else if(!s.isEmpty()) debug(s+" ("+(System.currentTimeMillis()-speed)+" ms)");speed=System.currentTimeMillis();}
    public void speed(){speed("Speed");}
    public void speedStart(){speed_intervalo_k=0;}
    public void speedFrom(){speed_intervalo_k=System.currentTimeMillis();}
    public void speedTo(){speed_intervalo=speed_intervalo+System.currentTimeMillis()-speed_intervalo_k;}
    public void speedCalc(String s){debug(s+": "+speed_intervalo+" ms");}

}
